﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Diagnostics;
using TextFlow.Activities;

namespace TextFlow.Activities
{
    internal delegate object ServiceLookupDelegate( Type serviceType );

    internal static class Utils
    {
        public static object ComputeResult( Expression expr, ServiceLookupDelegate svcLookup )
        {
            Debug.Assert( expr != null );
            Debug.Assert( svcLookup != null );

            IList<ParameterExpression> parms = null;

            Delegate fxn = CompileExpression( expr, out parms );

            Debug.Assert( parms != null );

            List<object> inputs = new List<object>();

            foreach ( ParameterExpression parm in parms )
            {
                inputs.Add( svcLookup( parm.Type ) );
            }

            object result = fxn.DynamicInvoke( inputs.ToArray() );

            return result;
        }

        private static Delegate CompileExpression( Expression expr, out IList<ParameterExpression> parameters )
        {
            if ( expr == null )
            {
                throw new ArgumentNullException( "expr" );
            }

            List<ParameterExpression> parms = new List<ParameterExpression>();

            Action<Expression> action = delegate( Expression e )
            {
                if ( e is ParameterExpression )
                {
                    parms.Add( e as ParameterExpression );
                }
            };

            VisitNodes( expr, action );

            parameters = parms;

            LambdaExpression lambda = Expression.Lambda( expr, parms.ToArray() );

            return lambda.Compile();
        }

        private static void VisitNodes( Expression expr, Action<Expression> action )
        {
            action( expr );

            if ( expr is BinaryExpression )
            {
                VisitNodes( ( expr as BinaryExpression ).Left, action );
                VisitNodes( ( expr as BinaryExpression ).Right, action );
            }
            else if ( expr is UnaryExpression )
            {
                VisitNodes( ( expr as UnaryExpression ).Operand, action );
            }
            else if ( expr is MethodCallExpression )
            {
                VisitNodes( ( expr as MethodCallExpression ).Object, action );

                foreach ( Expression arg in ( expr as MethodCallExpression ).Arguments )
                {
                    VisitNodes( arg, action );
                }
            }
            else if ( expr is MemberExpression )
            {
                VisitNodes( ( expr as MemberExpression ).Expression, action );
            }
        }
    }
}
